package com.sifou.courses.listener;

import com.sifou.courses.dto.SeckillOrderDto;
import com.sifou.courses.event.TradeFailureEvent;
import com.sifou.courses.service.IGoodsService;
import com.sifou.courses.service.IOrderService;
import com.sifou.courses.service.ISeckillService;
import com.sifou.courses.service.ITradeCallBackService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Closeable;
import java.io.IOException;

/**
 * @author liuzhongxu
 * @date 2020/8/19
 */
@Slf4j
@Component
public class SeckillListener implements InitializingBean, Closeable {

    @Resource
    private IGoodsService goodsService;

    @Resource
    private ITradeCallBackService tradeCallBackService;

    @Resource
    private IOrderService orderService;

    @Value("${seckill.goods.init}")
    private boolean initCache;

    @Value("${seckill.trade.callback}")
    private boolean tradeCallBack;

    @Autowired
    private ThreadPoolTaskExecutor poolTaskExecutor;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 秒杀商品预热
        if (initCache) {
            goodsService.initCache();
        }

        if (tradeCallBack) {
            //  初始化线程池
            poolTaskExecutor.setThreadNamePrefix("tradeCallBack-");
            poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);

            log.info("支付回调工作线程池，初始化成功");
            //  支付回调队列监听
            OrderSuccessWorker successWorker = new OrderSuccessWorker();
            OrderFailureWorker failureWorker = new OrderFailureWorker();
            poolTaskExecutor.execute(successWorker);
            poolTaskExecutor.execute(failureWorker);
        }
    }

    @Override
    public void close() throws IOException {
        if (tradeCallBack) {
            while (true) {
                if (ITradeCallBackService.paySuccess.size() > 0 || ITradeCallBackService.payFailure.size() > 0) {
                    try {
                        log.info("paySuccessQueue > 0 | payFailureQueue > 0");
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        //
                    }
                    continue;
                }

                break;
            }

            log.info("paySuccessQueue closed");
            log.info("payFailureQueue closed");
        }
    }

    class OrderSuccessWorker implements Runnable {

        @Override
        public void run() {
            log.info("启动支付回调监听器 OrderSuccessWorker");
            while (true) {
                Integer orderId = null;
                try {
                    orderId = ITradeCallBackService.paySuccess.take();
                    SeckillOrderDto orderDto = orderService.getOrder(orderId);
                    log.info("支付成功：" + orderDto);
                    tradeCallBackService.doCallBackSuccess(orderDto);
                } catch (InterruptedException e) {
                    log.error(e.getMessage(), e);
                }
            }

        }
    }

    class OrderFailureWorker implements Runnable {

        @Override
        public void run() {
            log.info("启动支付回调监听器 OrderFailureWorker");
            while (true) {
                TradeFailureEvent failureEvent = null;
                try {
                    failureEvent = ITradeCallBackService.payFailure.take();
                    Integer orderId = failureEvent.getOrderId();
                    ISeckillService seckillService = failureEvent.getSeckillService();
                    SeckillOrderDto orderDto = orderService.getOrder(orderId);
                    log.info("支付失败：" + orderDto);
                    tradeCallBackService.doCallBackFailure(seckillService, orderDto);
                } catch (InterruptedException e) {
                    log.error(e.getMessage(), e);
                }
            }

        }
    }
}
